package butines.core.game;

import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.KEY_COLOR_RENDERING;
import static java.awt.RenderingHints.KEY_INTERPOLATION;
import static java.awt.RenderingHints.KEY_RENDERING;
import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY;
import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR;
import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;

import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.image.BufferStrategy;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

public class Display {

	private GraphicsDevice graphicsDevice;
	private Window window;
	private Graphics2D graphics;
	private BufferStrategy bufferStrategy;
	
	private String displayMode;
	private boolean fullScreen;
	
	private DisplayMode desktop;
	private DisplayMode current;
	
	private List<DisplayMode> displayModes = new ArrayList<DisplayMode>();
	
	public Display(GraphicsDevice graphicsDevice, Window window) {
		this.graphicsDevice = graphicsDevice;
		this.window = window;
		
		desktop = graphicsDevice.getDisplayMode();
		for (DisplayMode displayMode: graphicsDevice.getDisplayModes()) {
			displayModes.add(displayMode);
		}
		
	}
	
	private void applyFullScreenMode(boolean value) {
		if (value) {
			if (graphicsDevice.isFullScreenSupported()) {	
				window.setVisible(false);
				graphicsDevice.setFullScreenWindow(window);
				fullScreen = true;
			} else {
				fullScreen = false;
			}
		} else {
			graphicsDevice.setFullScreenWindow(null);
			window.setVisible(true);
			fullScreen = false;
		}
	}
	
	private void applyDisplayMode(DisplayMode displayMode) {
		if (fullScreen) {
			if (graphicsDevice.isDisplayChangeSupported()) {
				try {
					graphicsDevice.setDisplayMode(displayMode);
					current = displayMode;
				} catch (IllegalArgumentException ex) {
					ex.printStackTrace();
					graphicsDevice.setFullScreenWindow(null);
					window.setVisible(true);
					fullScreen = false;
				}
			} else {
				graphicsDevice.setFullScreenWindow(null);
				window.setVisible(true);
				fullScreen = false;
			}
		} else {
			int left = (desktop.getWidth() - displayMode.getWidth()) / 2;
			int top = (desktop.getHeight() - displayMode.getHeight()) / 2;
			window.setLocation(left, top);
			window.setSize(displayMode.getWidth(), displayMode.getHeight());
			current = displayMode;
		}
		window.requestFocus();
	}
	
	private void createBuffserStrategy() {
		// avoid potential deadlock in 1.4.1_02
		try {
			EventQueue.invokeAndWait(new Runnable() {
				public void run() {
					window.createBufferStrategy(2);
				}
			});
		} catch (InterruptedException ex) {
			// ignore
		} catch (InvocationTargetException ex) {
			// ignore
		}
		bufferStrategy = window.getBufferStrategy();
	}

	private void createGraphics() {
		if (graphics != null) {
			graphics.dispose();
		}
		graphics = (Graphics2D) bufferStrategy.getDrawGraphics();
		graphics.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
		graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR);
		graphics.setRenderingHint(KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY);
		graphics.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
		graphics.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_OFF);
		graphics.setBackground(Color.BLACK);
	}
	
	private void formatCurrentDiplayMode() {
		this.displayMode = current.getWidth() + "x"
			+ current.getHeight() + "x"
			+ current.getBitDepth() + "@"
			+ current.getRefreshRate();
	}
	
	// TODO: Gerar evento informado que o display mode foi alterado, pois vai dar problema na UI
	private void applyResolution(DisplayMode displayMode, boolean fullScreen) {
		applyFullScreenMode(fullScreen);
		applyDisplayMode(displayMode);
		createBuffserStrategy();
		createGraphics();
		formatCurrentDiplayMode();
	}
	
	public void init() {
		if (current == null) {
			applyResolution(desktop, false);
		}
	}
	
	public Graphics2D getGraphics() {
		return graphics;
	}

	
	public void update() {
		if (graphics != null) {
			graphics.dispose();
			graphics = null;
		}
		
		if (bufferStrategy.contentsLost() == false) {
			bufferStrategy.show();
			Toolkit.getDefaultToolkit().sync();		
		}

		createGraphics();
	}
	
	public void destroy() {
		if (graphics != null) {
			graphics.dispose();
		}
		if (fullScreen) {
			graphicsDevice.setDisplayMode(desktop);
			graphicsDevice.setFullScreenWindow(null);
		} else {
			window.setVisible(false);
		}
		window.dispose();
	}

	public void setResolution(int width, int height, int bitDepth, int refreshRate, boolean fullScreen) {
		applyResolution(new DisplayMode(width, height, bitDepth, refreshRate), fullScreen);
	}
	
	public void setResolution(int width, int height, int bitDepth, boolean fullScreen) {
		setResolution(width, height, bitDepth, desktop.getRefreshRate(), fullScreen);
	}
	
	public void setResolution(int width, int height, boolean fullScreen) {
		setResolution(width, height, desktop.getBitDepth(), desktop.getRefreshRate(), fullScreen);
	}

	public String getDisplayMode() {
		return displayMode;
	}

	public boolean isFullScreen() {
		return fullScreen;
	}
	
	public int getWidth() {
		return current.getWidth();
	}

	public int getHeight() {
		return current.getHeight();
	}

	public int getBitDepth() {
		return current.getBitDepth();
	}

	public int getRefeshRate() {
		return current.getRefreshRate();
	}

	public int getX() {
		return window.getX();
	}

	public int getY() {
		return window.getY();
	}

	public void setLocation(int newx, int newy) {
		window.setLocation(newx, newy);
	}
	
}
